(function(window) {

	var components = [];

	// New, holds the 8 tiny boxes that will be our selection handles
	// the selection handles will be in this order:
	// 0 1 2
	// 3 4
	// 5 6 7
	var selectionHandles = [];

	// Hold canvas information
	var canvas;
	var ctx;
	var WIDTH;
	var HEIGHT;
	var INTERVAL = 20; // how often, in milliseconds, we check to see if a
	// redraw is needed

	var isDrag = false;
	var isResizeDrag = false;
	var expectResize = -1; // New, will save the # of the selection handle if
	// the mouse is over one.
	var mx, my; // mouse coordinates

	// when set to true, the canvas will redraw everything
	// invalidate() just sets this to false right now
	// we want to call invalidate() whenever we make a change
	var canvasValid = false;

	// The node (if any) being selected.
	// If in the future we want to select multiple objects, this will get turned
	// into an array
	var mySel = null;

	// The selection color and width. Right now we have a red selection with a
	// small width

	var mySelWidth = 2;
	var mySelBoxColor = 'darkred'; // New for selection boxes
	var mySelBoxSize = 6;

	// we use a fake canvas to draw individual shapes for selection testing
	var ghostcanvas;
	var gctx; // fake canvas context

	// since we can drag from anywhere in a node
	// instead of just its x/y corner, we need to save
	// the offset of the mouse when we start dragging.
	var offsetx, offsety;

	// Padding and border style widths for mouse offsets
	var stylePaddingLeft, stylePaddingTop, styleBorderLeft, styleBorderTop;

	/**
	 * Prototype Components
	 */

	// New methods on the Box class
	CImage.prototype = {
		// we used to have a solo draw function
		// but now each box is responsible for its own drawing
		// mainDraw() will call this with the normal canvas
		// myDown will call this with the ghost canvas with 'black'
		draw : function(context) {
			if (this.show) {
				// We can skip the drawing of elements that have moved off the
				// screen:
				if (this.x > WIDTH || this.y > HEIGHT)
					return;
				if (this.x + this.w < 0 || this.y + this.h < 0)
					return;

				var img = new Image;
				img.src = this.src;

				if ((this.x + this.w) < 400 && (this.y + this.h) < 400) {
					context.drawImage(img, this.x, this.y, this.w, this.h);
					this.xi = this.x;
					this.yi = this.y;
					this.hi = this.h;
					this.wi = this.w;
				} else {
					context.drawImage(img, this.xi, this.yi, this.wi, this.hi);
					this.x = this.xi;
					this.y = this.yi;
					this.h = this.hi;
					this.w = this.wi;
				}

				// draw selection
				// this is a stroke along the box and also 8 new selection
				// handles
				if (mySel === this) {

					context.lineWidth = mySelWidth;

					drawDashedRect(context, this.x, this.y, this.x + this.w,
							this.y + this.h, [ 5, 5 ]);

					// draw the boxes

					var half = mySelBoxSize / 2;

					// 0 1 2
					// 3 4
					// 5 6 7

					// top left, middle, right
					selectionHandles[0].x = this.x - half;
					selectionHandles[0].y = this.y - half;

					selectionHandles[1].x = this.x + this.w / 2 - half;
					selectionHandles[1].y = this.y - half;

					selectionHandles[2].x = this.x + this.w - half;
					selectionHandles[2].y = this.y - half;

					// middle left
					selectionHandles[3].x = this.x - half;
					selectionHandles[3].y = this.y + this.h / 2 - half;

					// middle right
					selectionHandles[4].x = this.x + this.w - half;
					selectionHandles[4].y = this.y + this.h / 2 - half;

					// bottom left, middle, right
					selectionHandles[6].x = this.x + this.w / 2 - half;
					selectionHandles[6].y = this.y + this.h - half;

					selectionHandles[5].x = this.x - half;
					selectionHandles[5].y = this.y + this.h - half;

					selectionHandles[7].x = this.x + this.w - half;
					selectionHandles[7].y = this.y + this.h - half;

					context.fillStyle = mySelBoxColor;
					for ( var i = 0; i < 8; i++) {
						var cur = selectionHandles[i];
						context.fillRect(cur.x, cur.y, mySelBoxSize,
								mySelBoxSize);
					}
				}
			}

		}

	};

	// New methods on the Box class
	CText.prototype = {
		// we used to have a solo draw function
		// but now each box is responsible for its own drawing
		// mainDraw() will call this with the normal canvas
		// myDown will call this with the ghost canvas with 'black'
		draw : function(context) {

			if (this.show) {
				// We can skip the drawing of elements that have moved off the
				// screen:
				if (this.x > WIDTH || this.y > HEIGHT)
					return;
				if (this.x + this.w < 0 || this.y + this.h < 0)
					return;

				context.fillStyle = this.fillStyle;

				context.font = this.font;
				context.textBaseline = this.textBaseline;

				if (this.fill) {
					context.fillText(this.text, this.x, this.y);
				} else {
					context.strokeText(this.text, this.x, this.y);
				}
			}
		}

	};

	/**
	 * Draw Utils
	 */
	function drawDashedLine(context, fromX, fromY, toX, toY, dashPattern) {

		context.beginPath();

		var dx = toX - fromX;
		var dy = toY - fromY;
		var angle = Math.atan2(dy, dx);
		var x = fromX;
		var y = fromY;
		context.moveTo(fromX, fromY);
		var idx = 0;
		var draw = true;
		while (!((dx < 0 ? x <= toX : x >= toX) && (dy < 0 ? y <= toY
				: y >= toY))) {
			var dashLength = dashPattern[idx++ % dashPattern.length];
			var nx = x + (Math.cos(angle) * dashLength);
			x = dx < 0 ? Math.max(toX, nx) : Math.min(toX, nx);
			var ny = y + (Math.sin(angle) * dashLength);
			y = dy < 0 ? Math.max(toY, ny) : Math.min(toY, ny);
			if (draw) {
				context.lineTo(x, y);
			} else {
				context.moveTo(x, y);
			}
			draw = !draw;
		}

		context.closePath();
		context.stroke();
	}
	;

	function drawDashedRect(context, fromX, fromY, toX, toY, dashPattern) {
		drawDashedLine(context, fromX, fromY, fromX, toY, dashPattern);
		drawDashedLine(context, fromX, toY, toX, toY, dashPattern);
		drawDashedLine(context, toX, toY, toX, fromY, dashPattern);
		drawDashedLine(context, toX, fromY, fromX, fromY, dashPattern);
	}
	;

	/**
	 * public method
	 */

	function drawcontroller(data) {

		canvas = document.getElementById(data.root);
		HEIGHT = canvas.height;
		WIDTH = canvas.width;
		ctx = canvas.getContext('2d');
		ghostcanvas = document.createElement('canvas');
		ghostcanvas.height = HEIGHT;
		ghostcanvas.width = WIDTH;
		gctx = ghostcanvas.getContext('2d');

		// fixes a problem where double clicking causes text to get selected on
		// the canvas
		canvas.onselectstart = function() {
			return false;
		}

		// fixes mouse co-ordinate problems when there's a border or padding
		// see getMouse for more detail
		if (document.defaultView && document.defaultView.getComputedStyle) {
			stylePaddingLeft = parseInt(document.defaultView.getComputedStyle(
					canvas, null)['paddingLeft'], 10) || 0;
			stylePaddingTop = parseInt(document.defaultView.getComputedStyle(
					canvas, null)['paddingTop'], 10) || 0;
			styleBorderLeft = parseInt(document.defaultView.getComputedStyle(
					canvas, null)['borderLeftWidth'], 10) || 0;
			styleBorderTop = parseInt(document.defaultView.getComputedStyle(
					canvas, null)['borderTopWidth'], 10) || 0;
		}

		// make mainDraw() fire every INTERVAL milliseconds
		setInterval(mainDraw, INTERVAL);

		// set our events. Up and down are for dragging,
		// double click is for making new boxes
		canvas.onmousedown = myDown;
		canvas.onmouseup = myUp;
		// canvas.mouseover = myOver;
		canvas.onmousemove = myMove;

		// set up the selection handle boxes
		for ( var i = 0; i < 8; i++) {
			var comp = new CImage;
			selectionHandles.push(comp);
		}
	}

	/**
	 * Object Constructors
	 */

	// Initialize a new Box, add it, and invalidate the canvas
	function addImage(x, y, w, h, src) {

		var comp = new CImage();
		comp.x = x;
		comp.y = y;
		comp.w = w
		comp.h = h;
		comp.src = src;
		comp.show = true;
		components.push(comp);
		invalidate();
	}

	// Initialize a new Box, add it, and invalidate the canvas
	function addText(x, y, text, fillStyle, font, textBaseline, fill) {

		var comp = new CText;
		comp.x = x;
		comp.y = y;
		comp.fillStyle = fillStyle;
		comp.textBaseline = textBaseline;
		comp.font = font;
		comp.text = text;
		comp.fill = fill;
		comp.show = true;
		components.push(comp);
		invalidate();
	}

	/**
	 * Main draw loop
	 */

	// While draw is called as often as the INTERVAL variable demands,
	// It only ever does something if the canvas gets invalidated by our code
	function mainDraw() {

		if (canvasValid == false) {
			clear(ctx);

			// Add stuff you want drawn in the background all the time here

			// draw all boxes
			var l = components.length;
			for ( var i = 0; i < l; i++) {
				components[i].draw(ctx); // we used to call drawshape, but
				// now
				// each box draws itself
			}

			canvasValid = true;
		}
	}

	/**
	 * states
	 */

	// wipes the canvas context
	function clear(c) {
		c.clearRect(0, 0, WIDTH, HEIGHT);
	}

	function invalidate() {
		canvasValid = false;
	}

	/**
	 * Mouse Actions
	 */

	// Happens when the mouse is moving inside the canvas
	function myMove(e) {
		if (isDrag) {
			getMouse(e);

			mySel.x = mx - offsetx;
			mySel.y = my - offsety;

			// something is changing position so we better invalidate the
			// canvas!
			invalidate();
		} else if (isResizeDrag) {
			// time ro resize!
			var oldx = mySel.x;
			var oldy = mySel.y;

			// 0 1 2
			// 3 4
			// 5 6 7
			switch (expectResize) {
			case 0:
				mySel.x = mx;
				mySel.y = my;
				mySel.w += oldx - mx;
				mySel.h += oldy - my;
				break;
			case 1:
				mySel.y = my;
				mySel.h += oldy - my;
				break;
			case 2:
				mySel.y = my;
				mySel.w = mx - oldx;
				mySel.h += oldy - my;
				break;
			case 3:
				mySel.x = mx;
				mySel.w += oldx - mx;
				break;
			case 4:
				mySel.w = mx - oldx;
				break;
			case 5:
				mySel.x = mx;
				mySel.w += oldx - mx;
				mySel.h = my - oldy;
				break;
			case 6:
				mySel.h = my - oldy;
				break;
			case 7:
				mySel.w = mx - oldx;
				mySel.h = my - oldy;
				break;
			}

			invalidate();
		}

		getMouse(e);
		// if there's a selection see if we grabbed one of the selection handles
		if (mySel !== null && !isResizeDrag) {

			for ( var i = 0; i < 8; i++) {
				// 0 1 2
				// 3 4
				// 5 6 7
				var cur = selectionHandles[i];

				// we dont need to use the ghost context because
				// selection handles will always be rectangles
				if (mx >= cur.x && mx <= cur.x + mySelBoxSize && my >= cur.y
						&& my <= cur.y + mySelBoxSize) {
					// we found one!
					expectResize = i;
					invalidate();
					switch (i) {
					case 0:
						this.style.cursor = 'nw-resize';
						break;
					case 1:
						this.style.cursor = 'n-resize';
						break;
					case 2:
						this.style.cursor = 'ne-resize';
						break;
					case 3:
						this.style.cursor = 'w-resize';
						break;
					case 4:
						this.style.cursor = 'e-resize';
						break;
					case 5:
						this.style.cursor = 'sw-resize';
						break;
					case 6:
						this.style.cursor = 's-resize';
						break;
					case 7:
						this.style.cursor = 'se-resize';
						break;
					}
					return;
				}

			}
			// not over a selection box, return to normal
			isResizeDrag = false;
			expectResize = -1;
			this.style.cursor = 'auto';
		}

		// Mark pointer with a 'move' style
		var l = components.length;
		this.style.cursor = 'auto';
		for ( var i = l - 1; i >= 0; i--) {
			// draw shape onto ghost context
			components[i].draw(gctx, 'black');

			// get image data at the mouse x,y pixel

			var imageData = gctx.getImageData(mx, my, 1, 1);
			var index = (mx + my * imageData.width) * 4;
			data = imageData.data;

			// if the mouse pixel exists, select and break
			if (imageData.data[3] > 0) {
				this.style.cursor = 'move';
				invalidate();
				clear(gctx);
			}
		}
	}

	// Happens when the mouse is clicked in the canvas
	function myDown(e) {
		getMouse(e);

		// toma el evento del click derecho
		if (!e)
			e = window.event;
		if (e.which)
			rightclick = (e.which == 3);
		else if (e.button)
			rightclick = (e.button == 2);

		// we are over a selection box
		if (expectResize !== -1) {
			isResizeDrag = true;
			return;
		}

		clear(gctx);
		var l = components.length;
		for ( var i = l - 1; i >= 0; i--) {
			// draw shape onto ghost context
			components[i].draw(gctx, 'black');

			// get image data at the mouse x,y pixel

			var imageData = gctx.getImageData(mx, my, 1, 1);
			var index = (mx + my * imageData.width) * 4;
			data = imageData.data;

			// if the mouse pixel exists, select and break
			if (imageData.data[3] > 0) {
				mySel = components[i];
				offsetx = mx - mySel.x;
				offsety = my - mySel.y;
				mySel.x = mx - offsetx;
				mySel.y = my - offsety;

				if (rightclick) {
					mySel.show = false;
				}

				isDrag = true;
				invalidate();
				clear(gctx);
				return;
			}

		}
		// havent returned means we have selected nothing
		mySel = null;
		// clear the ghost canvas for next time
		clear(gctx);
		// invalidate because we might need the selection border to disappear
		invalidate();
	}

	function myUp() {
		isDrag = false;
		isResizeDrag = false;
		expectResize = -1;
	}

	// Sets mx,my to the mouse position relative to the canvas
	// unfortunately this can be tricky, we have to worry about padding and
	// borders
	function getMouse(e) {
		var element = canvas, offsetX = 0, offsetY = 0;

		if (element.offsetParent) {
			do {
				offsetX += element.offsetLeft;
				offsetY += element.offsetTop;
			} while ((element = element.offsetParent));
		}

		// Add padding and border style widths to offset
		offsetX += stylePaddingLeft;
		offsetY += stylePaddingTop;

		offsetX += styleBorderLeft;
		offsetY += styleBorderTop;

		mx = e.pageX - offsetX;
		my = e.pageY - offsetY
	}

	window.drawcontroller = drawcontroller;
	window.addImage = addImage;
	window.addText = addText;
})(window);

drawcontroller({
	root : constants_id_graph
});
